gtkgstsink.c: Support EGL on Windows as well
authorChun-wei Fan <fanchunwei@src.gnome.org>
Mon, 17 May 2021 08:04:53 +0000 (16:04 +0800)
committerChun-wei Fan <fanchunwei@src.gnome.org>
Mon, 24 May 2021 11:04:14 +0000 (19:04 +0800)
Add support to look for and use the EGL context in Windows if it was activated
instead of desktop OpenGL.

GstGL may have been built with or without EGL/libANGLE support, so if it were,
check in GstGL whether we have gst_gl_display_new_with_type() to create a
GstGLDisplay that is of the GST_GL_WINDOW_WIN32 type when we are using
Desktop OpenGL (WGL), otherwise we show messages indicating that envvars
need to be set to initialize GstGL properly.

Due to a bug in GstGL, the GstGLContext can only be set up successfully
if one of the following is true:

*  An OpenGL 3.x or later emulator, such as Mesa is used (for WGL)
*  The latest GstGL master is being used, at the time of writing (for
   WGL)
*  GTK, libepoxy and GstGL are all built only with WGL support (for WGL)
*  EGL is being used in GTK at runtime

Special thanks to Matthew Waters for the help during the process.

modules/media/gtkgstsink.c
modules/media/meson.build

index a08e4646c7b8f49e827d1bab1516202303d318e3..8775ec54f4e198d08b6498c9902314028322da39 100644 (file)
@@ -31,9 +31,6 @@
 #if GST_GL_HAVE_PLATFORM_GLX
 #include <gst/gl/x11/gstgldisplay_x11.h>
 #endif
-#if GST_GL_HAVE_PLATFORM_EGL
-#include <gst/gl/egl/gstgldisplay_egl.h>
-#endif
 #endif
 
 #if GST_GL_HAVE_WINDOW_WAYLAND && GST_GL_HAVE_PLATFORM_EGL && defined (GDK_WINDOWING_WAYLAND)
 #include <gst/gl/wayland/gstgldisplay_wayland.h>
 #endif
 
-#if GST_GL_HAVE_WINDOW_WIN32 && GST_GL_HAVE_PLATFORM_WGL && defined (GDK_WINDOWING_WIN32)
+#if GST_GL_HAVE_WINDOW_WIN32 && (GST_GL_HAVE_PLATFORM_WGL || GST_GL_HAVE_PLATFORM_EGL) && defined (GDK_WINDOWING_WIN32)
 #include <gdk/win32/gdkwin32.h>
 #include <epoxy/wgl.h>
 #endif
 
+#if GST_GL_HAVE_PLATFORM_EGL && (GST_GL_HAVE_WINDOW_WIN32 || GST_GL_HAVE_WINDOW_X11)
+#include <gst/gl/egl/gstgldisplay_egl.h>
+#endif
+
 #include <gst/gl/gstglfuncs.h>
 
 enum {
@@ -353,11 +354,18 @@ gtk_gst_sink_show_frame (GstVideoSink *vsink,
   return GST_FLOW_OK;
 }
 
-#if GST_GL_HAVE_WINDOW_WIN32 && GST_GL_HAVE_PLATFORM_WGL && defined (GDK_WINDOWING_WIN32)
-#define HANDLE_EXTERNAL_WGL_MAKE_CURRENT epoxy_handle_external_wglMakeCurrent()
+#if GST_GL_HAVE_WINDOW_WIN32 && (GST_GL_HAVE_PLATFORM_WGL || GST_GL_HAVE_PLATFORM_EGL) && defined (GDK_WINDOWING_WIN32)
+#define HANDLE_EXTERNAL_WGL_MAKE_CURRENT(ctx) handle_wgl_makecurrent(ctx)
 #define DEACTIVATE_WGL_CONTEXT(ctx) deactivate_gdk_wgl_context(ctx)
 #define REACTIVATE_WGL_CONTEXT(ctx) reactivate_gdk_wgl_context(ctx)
 
+static void
+handle_wgl_makecurrent (GdkGLContext *ctx)
+{
+  if (!gdk_gl_context_get_use_es (ctx))
+    epoxy_handle_external_wglMakeCurrent();
+}
+
 static void
 deactivate_gdk_wgl_context (GdkGLContext *ctx)
 {
@@ -374,8 +382,37 @@ reactivate_gdk_wgl_context (GdkGLContext *ctx)
   if (!gdk_gl_context_get_use_es (ctx))
     gdk_gl_context_make_current (ctx);
 }
+
+/*
+ * Unfortunately, libepoxy does not offer a way to allow us to safely call
+ * gst_gl_context_get_current_gl_api() on a WGL context that underlies a
+ * GdkGLContext after we notify libepoxy an external wglMakeCurrent() has
+ * been called (which is required for the first gdk_gl_context_make_current()
+ * call in gtk_gst_sink_initialize_gl(), for instance), so we can't do
+ * gst_gl_context_get_current_gl_api() directly on WGL contexts that underlies
+ * GdkGLContext's.  So, we just ask GDK about our WGL context, since it already
+ * knows what kind of WGL context we have there...
+ */
+static gboolean
+check_win32_gst_gl_api (GdkGLContext  *ctx,
+                        GstGLPlatform *platform,
+                        GstGLAPI      *gl_api)
+{
+  gboolean is_gles = gdk_gl_context_get_use_es (ctx);
+
+  g_return_val_if_fail (*gl_api == GST_GL_API_NONE, FALSE);
+
+  *platform = is_gles ? GST_GL_PLATFORM_EGL : GST_GL_PLATFORM_WGL;
+
+  if (is_gles)
+    *gl_api = gst_gl_context_get_current_gl_api (*platform, NULL, NULL);
+  else
+    *gl_api = gdk_gl_context_is_legacy (ctx) ? GST_GL_API_OPENGL : GST_GL_API_OPENGL3;
+
+  return is_gles;
+}
 #else
-#define HANDLE_EXTERNAL_WGL_MAKE_CURRENT
+#define HANDLE_EXTERNAL_WGL_MAKE_CURRENT(ctx)
 #define DEACTIVATE_WGL_CONTEXT(ctx)
 #define REACTIVATE_WGL_CONTEXT(ctx)
 #endif
@@ -391,7 +428,7 @@ gtk_gst_sink_initialize_gl (GtkGstSink *self)
 
   display = gdk_gl_context_get_display (self->gdk_context);
 
-  HANDLE_EXTERNAL_WGL_MAKE_CURRENT;
+  HANDLE_EXTERNAL_WGL_MAKE_CURRENT (self->gdk_context);
   gdk_gl_context_make_current (self->gdk_context);
 
 #ifdef HAVE_GST_X11_SUPPORT
@@ -459,25 +496,55 @@ gtk_gst_sink_initialize_gl (GtkGstSink *self)
     }
   else
 #endif
-#if GST_GL_HAVE_WINDOW_WIN32 && GST_GL_HAVE_PLATFORM_WGL && defined (GDK_WINDOWING_WIN32)
-  if (GDK_IS_WIN32_DISPLAY (display) &&
-      !gdk_gl_context_get_use_es (self->gdk_context))
+#if GST_GL_HAVE_WINDOW_WIN32 && (GST_GL_HAVE_PLATFORM_WGL || GST_GL_HAVE_PLATFORM_EGL) && defined (GDK_WINDOWING_WIN32)
+  if (GDK_IS_WIN32_DISPLAY (display))
     {
-      platform = GST_GL_PLATFORM_WGL;
+      gboolean is_gles = check_win32_gst_gl_api (self->gdk_context, &platform, &gl_api);
+      const gchar *gl_type = is_gles ? "EGL" : "WGL";
 
-      GST_DEBUG_OBJECT (self, "got WGL on Win32!");
+      GST_DEBUG_OBJECT (self, "got %s on Win32!", gl_type);
 
-      gl_api = gst_gl_context_get_current_gl_api (platform, NULL, NULL);
       gl_handle = gst_gl_context_get_current_gl_context (platform);
 
       if (gl_handle)
         {
-          self->gst_display = gst_gl_display_new ();
+          /*
+           * We must force a win32 GstGL display type and if using desktop GL, the GL_Platform to be WGL
+           * and an appropriate GstGL API depending on the gl_api we receive.  We also ensure that we use
+           * an EGL GstGL API if we are using EGL in GDK.  Envvars are required, unless
+           * gst_gl_display_new_with_type() is available, unfortunately, so that gst_gl_display_new() does
+           * things correctly if we have GstGL built with both EGL and WGL support for the WGL case,
+           * otherwise gst_gl_display_new() will assume an EGL display, which won't work for us
+           */
+
+          if (gl_api & (GST_GL_API_OPENGL3 | GST_GL_API_OPENGL))
+            {
+#ifdef HAVE_GST_GL_DISPLAY_NEW_WITH_TYPE
+              self->gst_display = gst_gl_display_new_with_type (GST_GL_DISPLAY_TYPE_WIN32);
+#else
+              g_message ("If media fails to play, set the envvar `GST_DEBUG=1`, and if GstGL context creation fails");
+              g_message ("due to \"Couldn't create GL context: Cannot share context with non-EGL context\",");
+              g_message ("set in the environment `GST_GL_PLATFORM=wgl` and `GST_GL_WINDOW=win32`,");
+              g_message ("and restart the GTK application");
+
+              self->gst_display = gst_gl_display_new ();
+#endif
+            }
+
+#if GST_GL_HAVE_PLATFORM_EGL
+          else
+            {
+              gpointer display_ptr = gdk_win32_display_get_egl_display (display);
+              self->gst_display = GST_GL_DISPLAY (gst_gl_display_egl_new_with_egl_display (display_ptr));
+            }
+#endif
+
+          gst_gl_display_filter_gl_api (self->gst_display, gl_api);
           self->gst_app_context = gst_gl_context_new_wrapped (self->gst_display, gl_handle, platform, gl_api);
         }
       else
         {
-          GST_ERROR_OBJECT (self, "Failed to get handle from GdkGLContext, not using WGL");
+          GST_ERROR_OBJECT (self, "Failed to get handle from GdkGLContext, not using %s", gl_type);
              return;
         }
     }
@@ -498,7 +565,7 @@ gtk_gst_sink_initialize_gl (GtkGstSink *self)
       g_clear_error (&error);
       g_clear_object (&self->gst_app_context);
       g_clear_object (&self->gst_display);
-      HANDLE_EXTERNAL_WGL_MAKE_CURRENT;
+      HANDLE_EXTERNAL_WGL_MAKE_CURRENT (self->gdk_context);;
       return;
     }
   else
@@ -515,7 +582,7 @@ gtk_gst_sink_initialize_gl (GtkGstSink *self)
       g_clear_object (&self->gst_display);
     }
 
-  HANDLE_EXTERNAL_WGL_MAKE_CURRENT;
+  HANDLE_EXTERNAL_WGL_MAKE_CURRENT (self->gdk_context);
   REACTIVATE_WGL_CONTEXT (self->gdk_context);
 }
 
index 154390e7fec8eb85278fe69e9e9975fbe257b40b..1bd91821d8e86d759af35859126c78c74baf2467 100644 (file)
@@ -47,6 +47,21 @@ gstgl_dep = dependency('gstreamer-gl-1.0', version: '>= 1.12.3',
                        required: get_option('media-gstreamer'))
 
 if gstplayer_dep.found() and gstgl_dep.found()
+  extra_win_cflags = []
+
+  if host_machine.system() == 'windows'
+    new_gst_gl_display_code = \
+                '''#include <gst/gl/gstgldisplay.h>
+                   int main (int a, char ** g) {
+                     GstGLDisplay *d = gst_gl_display_new_with_type (GST_GL_DISPLAY_TYPE_WIN32);
+                     return 0;
+                   }'''
+    if cc.links(new_gst_gl_display_code, dependencies : gstgl_dep)
+      message('libgstgl has gst_gl_display_new_with_type()')
+      extra_win_cflags += '-DHAVE_GST_GL_DISPLAY_NEW_WITH_TYPE'
+    endif
+  endif
+
   media_backends += 'gstreamer'
   cdata.set('HAVE_GSTREAMER', 1)
   shared_module('media-gstreamer',
@@ -55,7 +70,7 @@ if gstplayer_dep.found() and gstgl_dep.found()
       'gtkgstpaintable.c',
       'gtkgstsink.c',
     ],
-    c_args: extra_c_args,
+    c_args: extra_c_args + extra_win_cflags,
     dependencies: [ libm, libgtk_dep, gstplayer_dep, gstgl_dep ],
     name_suffix: module_suffix,
     install_dir: media_install_dir,